class Statevar {

  String name, shortname, units = "";
  int length;
  float[] initial, current;
  float min = -Inf, max = Inf;
  boolean allocated = false;
  boolean advects = false, mixes = false, sinks = false;
   
  boolean hidden = true;

  Statevar() {}
  
  Statevar(String name, String shortname, String units) {
    this.name = name;
    this.shortname = shortname;
    this.units = units;
  }

  Statevar(String name, String shortname, String units, int N) {
    this.name = name;
    this.shortname = shortname;
    this.units = units;
    allocate(N);
  }
    
  void allocate(int N) {
    length = N;
    initial = new float[length];
    current = new float[length];
    allocated = true;
  }
  
  void setInitial(float a) {
    for (int i=0; i<length; i++) initial[i] = a;
  }
  
  void setInitial(float[] a) {
    if (a.length != length) {
      println(name + ": can't initialize a length " + length + " variable to a length " + a.length + " vector");
    } else {
      arrayCopy(a,initial);
    }
  }
  
  void setInitial(String filename, float[] zi) {
    if (zi.length != length) {
      println(name + ": can't initialize a length " + length + " variable to a length " + zi.length + " vector");
    } else {
      initial = interpFromFile(filename, zi);
    }
  }
  
  void rewind() {
    arrayCopy(initial, current);
    for (int i=0; i<length; i++) current[i] = max(min, current[i]);
  }
  
}




// ------------------------------------------------------------------------------------------------------------------




class Flux {

  String name, shortname, units = "";
  int length;
  Statevar from, to;
  float[] current;
  float min = -Inf, max = Inf;
  boolean hidden = true;
  boolean allocated = false;
  
  Flux(String name, String shortname, Statevar from, Statevar to) {
    create(name, shortname, from, to);
  }
  
  Flux(String name, String shortname, Statevar from, Statevar to, int N) {
    create(name, shortname, from, to);
    allocate(N);
  }
  
  void create(String name, String shortname, Statevar from, Statevar to) {
    this.name = name;
    this.shortname = shortname;
    this.from = from;
    this.to = to;
  }
  
  
  void allocate(int N) {
    current = new float[N];
    length = N;
    allocated = true;
  }
  
  void setUnits(Statevar time) {
    if (from != null) units = from.units + "/" + time.units;
    if ((from != null) && (to != null)) {
      if (!from.units.equals(to.units)) println("warning (" + shortname + ".setUnits): " + from.shortname + " is in " + from.units + " and " + to.shortname + " is in " + to.units);
    }
  }
  
  void constrainCurrent() {
    for (int i=0; i<length; i++) current[i] = constrain(current[i], min, max);
  }
  
}




// ----------------------------------------------------------------------------------------------------------------------------
  
  
  
  
  
class FreeParam {
  String name, shortname, units = "";
  float initial,min,max,current;
  boolean logScale = false;
  boolean highlight = false;
  Slider slider;
  boolean hidden = false;
  float quantization = 0;
  
  FreeParam() {}

  FreeParam(String name, String shortname, String units, float initial, float min, float max) {
    construct(name, shortname, units, initial, min, max);
  }
  
  void construct(String name, String shortname, String units, float initial, float min, float max) {
    if (units.equals("")) {
      this.name = name;
    } else {
      this.name = name + " (" + units + ")";
    }
    this.shortname = shortname;
    this.units = units;
    this.initial = initial;
    this.min = min;
    this.max = max;
  }
  
  void quantize(float q) {
    quantization = q;
  }
  
  void set(float val) {
    current = val;
    if (slider != null) slider.setVal(val);
  }
  
}




// ----------------------------------------------------------------------------------------------------------------------------




class State {

  int length;
  float[][] vars, fluxes;
  float[] params;
  Environment env;
  String errorMsg = "";

  State() {}
  
  State(Environment env) {
    this.env = env;
    vars = new float[env.vars.length][env.Ncells];
    fluxes = new float[env.fluxes.length][env.Ncells];
    params = new float[env.params.length];
  }
  
  float[] getVar(String S) {
    int i = getVarIndex(S);
    if (i >= 0) return vars[i];
    return null;
  }
  
  float getVar(String S, int k) {
    int i = getVarIndex(S);
    if (i >= 0) return vars[i][k];
    return 0./0;
  }
  
  float[] getFlux(String S) {
    int i = getFluxIndex(S);
    if (i >= 0) return fluxes[i];
    return null;
  }
  
  float getFlux(String S, int k) {
    int i = getFluxIndex(S);
    if (i >= 0) return fluxes[i][k];
    return 0./0;
  }

  float getParam(String S) {
    int i = getParamIndex(S);
    if (i >= 0) return params[i];
    return 0./0;
  }
  
  int getVarIndex(String S) {
    return env.getVarIndex(S);
  }
  
  int getFluxIndex(String S) {
    return env.getFluxIndex(S);
  }  
  
  int getParamIndex(String S) {
    return env.getParamIndex(S);
  }
  
}

